package project.platform.http;

import java.io.*;

import javax.microedition.io.*;

import project.Animal;
import project.platform.XML;

/**
 * 2BA7 Project HTTP Communications Platform to interact with servlet using an HttpConnection.
 *
 * @author Paul Magrath - 05379725
 */
public class DataPlatform {
	
	/** 
	 * Method to download animals from the servlet 
	 * @param myID The current user's unique identifier
	 * @param servletURL The web address of the servlet
	 * @return The Animals received from the servlet 
	 */
    public static Animal[] receiveFromServer(final String myID, final String servletURL){
    	final String result = DataPlatform.doRequest(myID,"","","receive",servletURL);
    	if (result.indexOf("<animal>")!=-1)
    		return XML.decodeFromXML(result);
    	DataPlatform.DEBUG("No animals downloaded.");
    	return null;
    }
    
	/** 
	 * Method to send an animal to the servlet.
	 * @param a Animal to send.
	 * @param to The name of the user the servlet is to give it to.
	 * @param from The current user's unique identifier.
	 * @return True or False depending on Success or Failure.
	 */
    public static boolean sendToServer(final Animal a, final String to, final String from, final String servletURL){
    	final String result = DataPlatform.doRequest(to,from,XML.encodeToXML(a),"send",servletURL);
    	if (result.indexOf("ERROR")==-1)
    		// if no error, return true.
    		return true;
    	return false;
    }
	
    /** 
     * Method that takes in the fields used by the servlet's form,
     * sends them to the servlet, and receives the servlet's response.
     * 
     * @param to Recipient's unique user identifier.
     * @param from The current user's unique identifier.
     * @param xml The XML-encoded Animal.
     * @param action Whether to "send" or "receive" from the server.
     * @param servletURL The web address of the servlet.
     * @return Success, XML encoded Animals, or Error message.
     */
    private static String doRequest(final String to, final String from, final String xml, final String action, final String servletURL){
	    HttpConnection c = null;
	    InputStream is = null;
	    String result = "";
	    try {
	    	c = DataPlatform.openHTTP(HttpConnection.POST,servletURL);
	    	DataPlatform.postToServlet(
	    			"to="+to+"&from="+from+"&xml="+xml+"&action="+action,
	    			c);
	        result = DataPlatform.checkResponseCode(c);
	        if ((result==null) | (result.length()==0)){
	        	// if no error sent by servlet, read html page sent as response
		        is = c.openInputStream();
		        DataPlatform.DEBUG("is = " + is);
		        result = DataPlatform.readHTML(c, is);        
	        }
	        DataPlatform.closeHTTP(c, is);
	        is = null;
	        c = null;	        
	    } catch (final IOException ex) {
	    	// Check for IO error.
	        result = "ERROR: " + DataPlatform.handleIOError(c, ex);
	    } catch (final IllegalArgumentException ille) {
	        // Check if an invalid proxy web server was detected.
	        result = "ERROR: " + ille.getMessage();
	    } catch (final Exception e) {
	    	// Check for anyother kind of error.
	    	result = "ERROR: " + e.toString();
	    }
	
	    DataPlatform.closeHTTP(c, is);
	    
	    return result;
	}

    /** 
     * Open http connection.
     * 
     * @param request POST or GET depending on type of request.
     * @param urlToOpen Web address of page to open.
     * @return HttpConnection instance.
     * @throws IOException
     */
	private static HttpConnection openHTTP(final String request, final String urlToOpen) throws IOException {
		HttpConnection c;
		DataPlatform.DEBUG(request + " Page: " + urlToOpen);
		c = (HttpConnection)Connector.open(urlToOpen);
		DataPlatform.DEBUG("c= " + c);

		c.setRequestMethod(request);
		c.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		c.setRequestProperty("foldedField", "first line\r\n second line\r\n third line");
		return c;
	}
   
	/** 
	 * Check response code sent back by server.
	 * 
	 * @param c HttpConnection to get response code on.
	 * @return Response code and message as a String.
	 * @throws IOException
	 */
    private static String checkResponseCode(final HttpConnection c) throws IOException {
		final StringBuffer b = new StringBuffer();
		final int rc = c.getResponseCode();
		if (rc != HttpConnection.HTTP_OK) {
		    b.append("Response Code: " + c.getResponseCode() + "\n");
		    b.append("Response Message: " + c.getResponseMessage() + "\n\n");
		}
		
		return b.toString();
	}
    
    /** 
     * Post data to servlet .
     * 
     * @param m String of data to send.
     * @param c HttpConnection to send data on.
     * @throws IOException
     */
	private static void postToServlet(final String m, final HttpConnection c) throws IOException {
		c.setRequestProperty("Content-Length", ""+ m.length());
		DataPlatform.DEBUG("Posting: " + m);
		final OutputStream os = c.openOutputStream();
		os.write(m.getBytes());
		os.close();
	}

	/** 
	 * Read HTML page sent back by server.
	 * 
	 * @param c HttpConnection to read HTML page from.
	 * @param is InputStream HTML page is connected to.
	 * @return HTML page as String.
	 * @throws IOException
	 */
	private static String readHTML(final HttpConnection c, final InputStream is) throws IOException {
		final StringBuffer buffer = new StringBuffer();
		int character;
		int contentLength;
		
		if (c instanceof HttpConnection) {
		    contentLength = (int)(c).getLength();
		    // Download the servlet generated page.
		    for (int i = 0; i < contentLength; i++) {
		    	character = is.read();
		        if (character <= ' ')
		            character = ' ';
		        buffer.append((char)character);
		    }
		}
		else
			contentLength = -1;

		DataPlatform.DEBUG("Content-Length = " + contentLength);
		String result = buffer.toString();
		DataPlatform.DEBUG("Downloaded = "+result);
		return result;
	}

    /** 
     * Close the http connection safely.
     * 
     * @param c HttpConnection to close.
     * @param is InputStream to close.
     */
    private static void closeHTTP(final HttpConnection c, final InputStream is) {
		try {
		    if (is != null)
				is.close();
	
		    if (c != null)
				c.close();
		} catch (final Exception ce) {
		    DataPlatform.DEBUG("Error closing connection");
		}
		if (is!=null)
			try {
	            is.available();
	            DataPlatform.DEBUG("Inputstream failed to throw IOException after close");
	        } catch (final IOException io) {
	            DataPlatform.DEBUG("expected IOException (available())");
	            io.printStackTrace();
	
	            // Test to make sure available() is only valid while
	            // the connection is still open.,
	        }
	}

    /** 
     * Handle IO errors that occur during the HTTP connection.
     * 
     * @param c HttpConnection exception occured on.
     * @param ex IOException that occured.
     * @return Verbose error message.
     */
	private static String handleIOError(final HttpConnection c, final IOException ex) {
		String errMsg = null;
		ex.printStackTrace();
		DataPlatform.DEBUG(ex.getClass().toString());
		DataPlatform.DEBUG(ex.toString());
		DataPlatform.DEBUG("Exception reading from http");
	
		if (c != null) {
		    try {
	
		        if (c instanceof HttpConnection)
					errMsg = (c).getResponseMessage();
	
		        DataPlatform.DEBUG(errMsg);
	
		        if (errMsg == null)
					errMsg = "No Response message";
		    } catch (final IOException e) {
		        e.printStackTrace();
	
		        errMsg = e.toString();
		        DataPlatform.DEBUG(errMsg);
	
		        if (errMsg == null)
					errMsg = ex.getClass().getName();
		    }
	
		    try {
		        c.close();
		    } catch (final IOException ioe) {
		        // do not over throw current exception
		    }
		} else
			errMsg = "Could not open URL";
		return errMsg;
	}

	/**
	 * Debug output routine.
	 * @param s string to be printed.
	 */
	private static final void DEBUG(final String s) {
	    if (true)
			System.out.println(s);
	}

}
